﻿using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Security.Cryptography;
using System.Text;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

namespace BfLib.Text
{
    public static class StringExtensionMethods
    {
        public static string[] MonthsOfTheYear = { String.Empty, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
        public static string[] MonthsOfTheYearShort = { String.Empty, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
        private static string AlphaNumericChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        #region Parsing

        public static double ToDoubleDef(this string s, double defaultValue)
        {
            double d = 0.0;
            if (double.TryParse(s, out d))
            {
                return d;
            }
            else
            {
                return defaultValue;
            }
        }

        public static float ToFloatDef(this string s, float defaultValue)
        {
            float d = 0.0F;
            if (float.TryParse(s, out d))
            {
                return d;
            }
            else
            {
                return defaultValue;
            }
        }

        public static decimal ToDecimalDef(this string s, decimal defaultValue)
        {
            decimal d = 0.0M;
            if (decimal.TryParse(s, out d))
            {
                return d;
            }
            else
            {
                return defaultValue;
            }
        }

        public static int ToIntDef(this string s, int defaultValue)
        {
            if (s == null) return defaultValue;
            int n = defaultValue;

            if (int.TryParse(s, out n))
            {
                return n;
            }
            else
                return defaultValue;
        }

        public static int ToInt32Def(this string s, Int32 defaultValue)
        {
            if (s == null) return defaultValue;
            Int32 n = defaultValue;

            if (Int32.TryParse(s, out n))
            {
                return n;
            }
            else
                return defaultValue;
        }

        public static Int16 ToInt16Def(this string s, Int16 defaultValue)
        {
            if (s == null) return defaultValue;

            Int16 n = defaultValue;

            if (Int16.TryParse(s, out n))
            {
                return n;
            }
            else
                return defaultValue;
        }

        public static Int16 ToByteDef(this string s, byte defaultValue)
        {
            if (s == null) return defaultValue;

            byte n = defaultValue;

            if (byte.TryParse(s, out n))
            {
                return n;
            }
            else
                return defaultValue;
        }

        public static Int64 ToInt64Def(this string s, Int64 defaultValue)
        {
            if (s == null) return defaultValue;

            Int64 n = defaultValue;

            if (Int64.TryParse(s, out n))
            {
                return n;
            }
            else
                return defaultValue;
        }
 
        #endregion

        #region HTML and URL encoding

        public static string HtmlEncode(this string s)
        {
            if (s.Equals(System.DBNull.Value))
                s = string.Empty;

            return System.Web.HttpUtility.HtmlEncode(s);
        }

        public static string HtmlDecode(this string s)
        {
            return System.Web.HttpUtility.HtmlDecode(s);
        }

        public static string UrlEncode(this string s)
        {

            return System.Web.HttpUtility.UrlEncode(s);
        }

        public static string UrlDecode(this string s)
        {
            return System.Web.HttpUtility.UrlDecode(s);
        }

        #endregion

        #region Cryptography

        public static string HashMD5(this string s)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[s.Length * 2];
            enc.GetBytes(s.ToCharArray(), 0, s.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] result = md5.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        public static string HashSHA1(this string s)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[s.Length * 2];
            enc.GetBytes(s.ToCharArray(), 0, s.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] result = sha1.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        public static string HashSHA256(this string s)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[s.Length * 2];
            enc.GetBytes(s.ToCharArray(), 0, s.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            SHA256 sha256 = new SHA256CryptoServiceProvider();
            byte[] result = sha256.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        public static string HashSHA384(this string s)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[s.Length * 2];
            enc.GetBytes(s.ToCharArray(), 0, s.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            SHA384 sha384 = new SHA384CryptoServiceProvider();
            byte[] result = sha384.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        public static string HashSHA512(this string s)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[s.Length * 2];
            enc.GetBytes(s.ToCharArray(), 0, s.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            SHA512 sha512 = new SHA512CryptoServiceProvider();
            byte[] result = sha512.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        public static string HashRIPEMD160(this string s)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[s.Length * 2];
            enc.GetBytes(s.ToCharArray(), 0, s.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            RIPEMD160 ripemd160 = new RIPEMD160Managed();// RIPEMD160CryptoServiceProvider();
            byte[] result = ripemd160.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        // Encrypt a string into a string using a password 
        //    Uses Encrypt(byte[], byte[], byte[]) 
        public static string Encrypt(this string clearText, string Password)
        {
            // First we need to turn the input string into a byte array. 
            byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);

            // Then, we need to turn the password into Key and IV 
            // We are using salt to make it harder to guess our key
            // using a dictionary attack - 
            // trying to guess a password by enumerating all possible words. 

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

            // Now get the key/IV and do the encryption using the
            // function that accepts byte arrays. 
            // Using PasswordDeriveBytes object we are first getting
            // 32 bytes for the Key 
            // (the default Rijndael key length is 256bit = 32bytes)
            // and then 16 bytes for the IV. 
            // IV should always be the block size, which is by default
            // 16 bytes (128 bit) for Rijndael. 
            // If you are using DES/TripleDES/RC2 the block size is
            // 8 bytes and so should be the IV size. 
            // You can also read KeySize/BlockSize properties off
            // the algorithm to find out the sizes. 

            byte[] encryptedData = BfLib.Text.Crypter.Encrypt(clearBytes,
                     pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string. 
            // A common mistake would be to use an Encoding class for that.
            //It does not work because not all byte values can be
            // represented by characters. 
            // We are going to be using Base64 encoding that is designed
            //exactly for what we are trying to do. 
            return Convert.ToBase64String(encryptedData);
        }

        // Decrypt a string into a string using a password 
        //    Uses Decrypt(byte[], byte[], byte[]) 
        public static string Decrypt(this string cipherText, string Password)
        {
            // First we need to turn the input string into a byte array. 
            // We presume that Base64 encoding was used 
            byte[] cipherBytes = Convert.FromBase64String(cipherText);

            // Then, we need to turn the password into Key and IV 
            // We are using salt to make it harder to guess our key
            // using a dictionary attack - 
            // trying to guess a password by enumerating all possible words. 

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 
            0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

            // Now get the key/IV and do the decryption using
            // the function that accepts byte arrays. 
            // Using PasswordDeriveBytes object we are first
            // getting 32 bytes for the Key 
            // (the default Rijndael key length is 256bit = 32bytes)
            // and then 16 bytes for the IV. 
            // IV should always be the block size, which is by
            // default 16 bytes (128 bit) for Rijndael. 
            // If you are using DES/TripleDES/RC2 the block size is
            // 8 bytes and so should be the IV size. 
            // You can also read KeySize/BlockSize properties off
            // the algorithm to find out the sizes. 

            byte[] decryptedData = BfLib.Text.Crypter.Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string. 
            // A common mistake would be to use an Encoding class for that.
            // It does not work 
            // because not all byte values can be represented by characters. 
            // We are going to be using Base64 encoding that is 
            // designed exactly for what we are trying to do. 
            return System.Text.Encoding.Unicode.GetString(decryptedData);
        }

        #endregion

        #region Checking and validation

        /// <summary>
        /// Returns the number of somma seperated integers in the string. Returns -1 if the 
        /// string does not consist purely of comma seperated integers. (spaces are allowed in string)
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int IsCommaDelimitedIntegers(this string s)
        {
            if (s == null) return -1;

            s = s.Trim();

            if (s.Length == 0) return -1;

            s = s.Replace(" ", string.Empty);
            int test = 0;

            string[] parts = s.Split(',');

            foreach (string temp in parts)
            {
                try
                {
                    if (!int.TryParse(temp, out test))
                    {
                        return -1;
                    }
                }
                catch
                {
                    return -1;
                }
            }
            return parts.Length;
        }

        public static int IsCommaDelimitedInt64s(this string s)
        {
            if (s == null) return -1;

            s = s.Trim();

            if (s.Length == 0) return -1;

            s = s.Replace(" ", string.Empty);
            Int64 test = 0;

            string[] parts = s.Split(',');

            foreach (string temp in parts)
            {
                try
                {
                    if (!Int64.TryParse(temp, out test))
                    {
                        return -1;
                    }
                }
                catch
                {
                    return -1;
                }
            }
            return parts.Length;
        }

        public static bool IsIntegerString(this string s)
        {
            Regex objIntPattern = new Regex("^[0-9]*$");
            return objIntPattern.IsMatch(s);
        }

        public static bool IsDoubleString(this string s)
        {
            double d = 0.0;
            return double.TryParse(s, out d);
        }

        public static bool IsValidEmailFormat(this string s)
        {
            s = s.Trim();
            if (s == null)
            {
                return false;
            }
            else if ((s.Length == 0) || (s.Length > 60))
            {
                return false;
            }
            else
            {
                return Regex.IsMatch(s, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.IgnorePatternWhitespace);
            }
        }

        public static bool IsInt(this string s)
        {
            if (s == null)
            {
                return false;
            }
            int num = 0;
            return int.TryParse(s, out num);
        }

        public static bool IsIntInRange(this string s, int iLow, int iHigh)
        {
            int num = 0;
            if (int.TryParse(s, out num))
            {
                return ((num >= iLow) && (num <= iHigh));
            }
            else
            {
                return false;
            }
        }

        public static bool IsGuid(this string s)
        {
            if (s != null)
            {
                return Regex.Match(s, @"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$").Success;
            }
            else
            {
                return false;
            }
        }

        public static bool IsValidHttpUrl(this string s)
        {
            string pattern = @"http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&amp;%\$#\=~])*[^\.\,\)\(\s]$";
            Regex reg = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
            return reg.IsMatch(s);
        }

        #endregion

        #region Parsing & Reading
        public static string GetPart(this string s, char delimiter, int partnum)
        {
            char[] delim = { delimiter };
            string[] parts = s.Split(delim);

            if (partnum > parts.Length)
            {
                return string.Empty;
            }
            else
            {
                return parts[partnum];
            }
        }

        public static int[] GetIntsFromCommaString(this string s)
        {
            char[] sep = { ',' };
            string[] selStrs = s.Split(sep);

            int temp;
            string tmpStr = string.Empty;

            int[] res = new int[selStrs.Length];

            for (int i = 0; i < selStrs.Length; i++)
            {
                tmpStr = selStrs[i].Trim();

                if (int.TryParse(tmpStr, out temp))
                {
                    res[i] = temp;
                }
                else
                {
                    res = new int[1];
                    res[0] = -1;
                    return res;
                }
            }

            return res;
        }

        public static string[] GetStringsFromCommaString(this string s)
        {
            char[] sep = { ',' };
            return s.Split(sep);
        }
     
        public static int GetCharCount(this string s, char c)
        {
            int result = 0;
            foreach (char ch in s)
            {
                if (ch == c)
                {
                    result++;
                }
            }

            return result;
        }

        public static int GetCommaListIndex(this string s, string value)
        {
            return Array.IndexOf(s.Split(','), value);
        }

        public static bool StringIsCommaSeparatedIntegers(this string s)
        {
            if (s == null) return false;

            char[] sep = { ',' };
            string[] selStrs = s.Split(sep);
            int temp;
            string tmpStr = string.Empty;

            foreach (string str in selStrs)
            {
                tmpStr = str.Trim();

                if (tmpStr.Length == 0)
                    continue;

                if (!int.TryParse(tmpStr, out temp))
                {
                    return false;
                }
            }

            return true;
        }

        #endregion

        #region Replacement and deletion


        public static string RemoveFileExtension(this string s)
        {
            int i = s.LastIndexOf('.');

            if (i < 0)
            {
                return s;
            }
            else
            {
                return s.Substring(0, i);
            }
        }

        public static string MakeFileNameSafe(this string s)
        {
            s = s.Replace(":", "_");
            s = s.Replace("?", "_");
            s = s.Replace("\\", "_");
            s = s.Replace("*", "_");
            s = s.Replace("<", "_");
            s = s.Replace(">", "_");
            s = s.Replace("/", "_");
            s = s.Replace("|", "_");
            s = s.Replace("\"", "_");

            return s;
        }

        public static string MinimizeSpaces(this string s)
        {
            while (s.IndexOf("  ") >= 0)
            {
                s = s.Replace("  ", " ");
            }

            return s.Trim();
        }

        public static string RemoveAllHtmlTagsAndLimitLength(this string s, int maxsize, bool AddEpsilon)
        {
            s = RemoveAllHtmlTags(s);

            if (s.Length == 0) return s;
            if (maxsize <= 0) return s;

            if (AddEpsilon)
            {
                return String.Format("{0}...", s.LimitLength(maxsize));
            }
            else
            {
                return s.LimitLength(maxsize);
            }
        }

        public static string LimitLength(this string s, int maxAllowedLength)
        {
            if (s == null)
            {
                return string.Empty;
            }
            else if (s.Length <= maxAllowedLength)
            {
                return s;
            }
            else
            {
                return s.Substring(0, maxAllowedLength).Trim();
            }
        }

        public static string LimitLengthEpsillon(this string s, int maxAllowedLength)
        {
            const string el = "...";
            if (s == null)
            {
                return string.Empty;
            }
            else if (s.Length <= maxAllowedLength - 3)
            {
                return s;
            }
            else if (s.Length < 3)
            {
                return el;
            }
            else
            {
                return String.Concat(s.Substring(0, maxAllowedLength - 3).Trim(), el);
            }
        }

        public static string RemoveUnsupportedHtmlTags(this string s)
        {
            if (s == null) { return string.Empty; }
            if (s.Length == 0) { return string.Empty; }

            s = s.HtmlDecode();
            string output = string.Empty;
            // break the comments so someone cannot add an open comment
            s = s.Replace("<!--", string.Empty);

            // strip out comments and doctype
            Regex docType = new Regex("<!DOCTYPE[.]*>");
            output = docType.Replace(s, string.Empty);

            // add target="_blank" to hrefs and remove parts that are 
            // not supported
            output = Regex.Replace(output, "(.*)", @"$5");

            //strip out most known tags except (a|b|br|blockquote|em|h1|h2|
            //h3|h4|h5|h6|hr|i|li|ol|p|u|ul|strong|sub|sup)
            Regex badTags = new Regex("< [/]{0,1}(abbr|acronym|address|applet|area|base|basefont|bdo|big|body|button|caption|center|cite|code|col|colgroup|dd|del|dir|div|dfn|dl|dt|embed|fieldset|font|form|frame|frameset|head|html|iframe|img|input|ins|isindex|kbd|label|legend|link|map|menu|meta|noframes|noscript|object|optgroup|option|param|pre|q|s|samp|script|select|small|span|strike|style|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|var|xmp){1}[.]*>");
            return badTags.Replace(output, string.Empty);
        }

        public static string RemoveAllHtmlTags(this string s)
        {
            if (s == null) { return string.Empty; }
            if (s.Length == 0) { return string.Empty; }

            s = Regex.Replace(s, "<(.|\n)*?>", string.Empty);
            s = s.Replace("#0026;", string.Empty);        //Unicode & or double decoded &amp;
            s = s.HtmlDecode().Trim();
            s = s.Replace("\"", "&quot;");
            //html = html.Replace("'", string.Empty);
            s = s.Replace(@"\", @"\\");
            s = s.Replace(@"\\\\", @"\\");
            //disable someoneputting a broken comment in to disable the rest of the page
            s = s.Replace("<", "&lt;");
            return s.Replace(">", "&gt;");

        }

        public static string RemoveHtmlTagsOnly(this string s)
        {
            if (s == null) { return string.Empty; }
            return Regex.Replace(s, "<(.|\n)*?>", string.Empty);
        }

        public static string StripTagsCharArray(this string s)
        {
            char[] array = new char[s.Length];
            int arrayIndex = 0;
            bool inside = false;

            for (int i = 0; i < s.Length; i++)
            {
                char let = s[i];
                if (let == '<')
                {
                    inside = true;
                    continue;
                }
                if (let == '>')
                {
                    inside = false;
                    continue;
                }
                if (!inside)
                {
                    array[arrayIndex] = let;
                    arrayIndex++;
                }
            }
            return new string(array, 0, arrayIndex);
        }

        public static string RemoveHTMLScripts(this string s)
        {
            return Regex.Replace(s, @"<script(.|\n)*?>(.|\n)*?</script>", string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
        }

        public static string RemoveHTMLComments(this string s)
        {
            return Regex.Replace(s, @"<!--(.|\n)*?-->", string.Empty, RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
        }

        #endregion

        #region Color methods

        public static System.Drawing.Color HtmlStrToColor(this string s)
        {
            try
            {
                return System.Drawing.ColorTranslator.FromHtml(s);
            }
            catch//(Exception ex)
            {
                return System.Drawing.Color.Black;
            }
        }
        #endregion

        #region Misc
        public static string Reverse(this string s)
        {
            char[] arr = s.ToCharArray();
            Array.Reverse(arr);
            return new string(arr);
        }

        public static string GetTextAfterLastInstanceOf(this string s, char c)
        {
            int i = s.LastIndexOf(c);

            if (i < 0)
                return string.Empty;

            return s.Substring(i + 1, s.Length - i - 1);
        }

        public static string Slugify(this string s)
        {
            s = s.ToLower();

            foreach (char c in s)
            {
                if (AlphaNumericChars.IndexOf(c) >= 0)
                {
                    s = s.Replace(c, '_');
                }
            }

            return String.Format("{0}_{1}", StringExtensionMethods.RandomIntegerString(5), s);
        }

        public static string ChangeFileExtension(this string s, string newExt)
        {
            return System.IO.Path.ChangeExtension(s, newExt);
        }
    
        #endregion

        /// <summary>
        /// Gets the value of an attribute from a HTML tag. It's more fault tolerant than XML parsers
        /// and it's quite fast too.
        /// </summary>
        /// <param name="tag">A HTML tag</param>
        /// <param name="attrib">The name of the HTML attribute</param>
        /// <returns>The value of the specified HTML attribute</returns>
        public static string GetAttibute(this string s, string attrib)
        {
            char quoteChar, nonQuoteChar;
            char quoteSingle = '\'', quoteDouble = '\"';
            int i, j;
            StringBuilder sb = new StringBuilder();

            s = ReduceAttributeSpaces(s);
            i = s.IndexOf(String.Concat(attrib, "="));

            if (i >= 0)
            {
                i = i + attrib.Length;
            }
            else
            {
                return string.Empty;
            }


            if ((s[i + 1] == quoteSingle) || (s[i + 1] == quoteDouble))
            {
                quoteChar = s[i + 1];
                if (quoteChar == quoteSingle) nonQuoteChar = quoteDouble;
                else nonQuoteChar = quoteSingle;


                j = i + 2;
                while ((s[j] != quoteChar) && (j < s.Length) && (s[j] != '>'))// && (s[j] != nonQuoteChar))
                {
                    sb.Append(s[j]);
                    j++;
                }
            }
            else
            {
                j = i + 1;
                while ((s[j] != ' ') && (j < s.Length) && (s[j] != '>'))
                {
                    sb.Append(s[j]);
                    j++;
                }
            }

            return sb.ToString();
        }

        public static Dictionary<string, string> GetAttributes(this string s)
        {

            string temp = s;
            temp = Regex.Replace(temp,  "\\\"(.|\\n)*?\\\"", string.Empty, RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
            temp = Regex.Replace(temp, @"\'(.|\n)*?\'", string.Empty, RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);


            //string regexp = @"\ {0,1}\'{0,1}([a-z0-9]*[^<>])=";
            string regexp = "\\ {0,1}\\'{0,1}\\\"{0,1}([a-z0-9]*[^<>])=";
            List<string> matches = StringLib.RegexpGetMatches(temp, regexp, 1);
            Dictionary<string, string> attributes = new Dictionary<string,string>();

            string mt = string.Empty;

            foreach(string m in matches)
            {
                mt= m.Trim();

                if (!attributes.ContainsKey(mt))
                {
                    attributes.Add(mt, s.GetAttibute(mt));
                }
                else
                {
                    attributes[mt] = String.Format("{0} {1}", attributes[mt], s.GetAttibute(mt)); 
                }
                
            }

            return attributes;
        }

        public static bool RegexpContains(this string s, string RegexpExpression)
        {
            return Regex.IsMatch(s, RegexpExpression, RegexOptions.IgnoreCase);
        }

        public static int RegexpGetMatchCount(this string s, string RegexpExpression)
        {
            Regex regex = new Regex(RegexpExpression, RegexOptions.IgnoreCase);
            int result = 0;
            MatchCollection mc = regex.Matches(s);
            foreach (Match m in mc)
            {
                result++;
            }
            return result;
        }

        private static void ReduceSpaces(this string s)
        {
            s = s.Replace("\t", string.Empty);
            while (s.IndexOf("   ") >= 0)
            {
                s = s.Replace("   ", " ");
            }
            while (s.IndexOf("  ") >= 0)
            {
                s = s.Replace("  ", " ");
            }

            while (s.IndexOf("= ") >= 0)
            {
                s = s.Replace("= ", "=");
            }
            while (s.IndexOf(" =") >= 0)
            {
                s = s.Replace(" =", "=");
            }
        }

        private static string ReduceAttributeSpaces(string s)
        {
            s = s.Replace("\t", string.Empty);
            while (s.IndexOf("   ") >= 0)
            {
                s = s.Replace("   ", " ");
            }
            while (s.IndexOf("  ") >= 0)
            {
                s = s.Replace("  ", " ");
            }

            while (s.IndexOf("= ") >= 0)
            {
                s = s.Replace("= ", "=");
            }
            while (s.IndexOf(" =") >= 0)
            {
                s = s.Replace(" =", "=");
            }

            return s;
        }

        private static string RandomIntegerString(int size)
        {
            string charSet = "0123456789";
            StringBuilder builder = new StringBuilder();
            Random random = new Random();
            for (int i = 0; i < size; i++)
            {
                builder.Append(charSet[random.Next(charSet.Length - 1)]);
            }
            return builder.ToString();
        }

        public static string StripQuotes(this string s)
        {
            return Regex.Replace(s, "\"\\s*(.|\\n)*?\"", string.Empty);
        }
    }
}
